package com.zengge.zbc.utils.http;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Component
public class HttpClientImpl implements HttpClient {

    private final static Logger logger = LoggerFactory.getLogger(HttpClientImpl.class);
    private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");

    private OkHttpClient client;
    private ObjectMapper objectMapper;
    private CallbackProxy callbackProxy;

    @Autowired
    public HttpClientImpl(final CallbackProxy callbackProxy) {
        OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
        clientBuilder.readTimeout(10, TimeUnit.SECONDS);
        clientBuilder.connectTimeout(10, TimeUnit.SECONDS);
        client = clientBuilder.build();
        this.callbackProxy = callbackProxy;
        objectMapper = new ObjectMapper();
    }


    private void setHeader(Request.Builder builder, Map<String, String> headers) {
        if (headers != null) {
            headers.forEach(builder::addHeader);
        }
    }

    @Override
    public void postJson(String url, Map<String, String> headers, Object object, Callback callback) {
        try {

            String json = objectMapper.writeValueAsString(object);
            Request.Builder builder = new Request.Builder()
                    .url(url)
                    .post(RequestBody.create(JSON, json));
            setHeader(builder, headers);
            client.newCall(builder.build()).enqueue(createCallback(callback));
        } catch (Throwable e) {
            callback.onFailure(e);
        }
    }

    @Override
    public void postFormBody(String url, Map<String, String> headers, FormBody formBody, Callback callback) {
        try {
            Request.Builder builder = new Request.Builder()
                    .url(url)
                    .post(formBody);
            setHeader(builder, headers);
            client.newCall(builder.build()).enqueue(createCallback(callback));
        } catch (Throwable e) {
            callback.onFailure(e);
        }
    }

    @Override
    public Response postFormBody(String url, Map<String, String> headers, FormBody formBody) throws IOException {
        Request.Builder builder = new Request.Builder()
                .url(url)
                .post(formBody);
        setHeader(builder, headers);
        return client.newCall(builder.build()).execute();
    }

    @Override
    public void get(String url, final Map<String, String> headers, Callback callback) {
        try {
            Request.Builder builder = new Request.Builder()
                    .url(url)
                    .get();
            setHeader(builder, headers);
            client.newCall(builder.build()).enqueue(createCallback(callback));
        } catch (Throwable e) {
            callback.onFailure(e);
        }
    }

    @Override
    public <T> T get(String url, Map<String, String> headers, TypeReference<T> type) {

        Response response = null;
        try {
            Request.Builder builder = new Request.Builder()
                    .url(url)
                    .get();
            setHeader(builder,headers);
            response = client.newCall(builder.build()).execute();
            ResponseBody responseBody = response.body();
            if(responseBody!=null && response.code() == 200){
                String json = responseBody.string();
                return objectMapper.readValue(json, type);
            }
            throw new RemoteException(response.code(),response.message());
        } catch (IOException e) {
            throw new RemoteException(-1,e.getMessage());
        } finally {
            if(response!=null){
                response.close();
            }
        }
    }

    private okhttp3.Callback createCallback(Callback callback) {

        return new okhttp3.Callback() {
            @Override
            public void onFailure(Call call, IOException e) {


                try {
                    callbackProxy.onFailure(callback, e);
                } catch (Throwable ex) {
                    logger.error(ex.getMessage(), ex);
                }
            }

            @Override
            public void onResponse(Call call,Response response) {
                callbackProxy.onSucceed(callback, response);
            }
        };
    }
}
